package ph.com.gs3.formalistics;

import java.util.ArrayList;
import java.util.List;

import ph.com.gs3.formalistics.DocumentActivity.DocumentType;
import ph.com.gs3.formalistics.adapters.DocumentListItemViewAdapter.DocumentListItemActionListener;
import ph.com.gs3.formalistics.fragments.dialogs.FormSelectionDialogFragment;
import ph.com.gs3.formalistics.fragments.dialogs.FormSelectionDialogFragment.FormsAvailability;
import ph.com.gs3.formalistics.fragments.views.MainViewFragment;
import ph.com.gs3.formalistics.fragments.views.MainViewFragment.MainViewActivityActionListener;
import ph.com.gs3.formalistics.fragments.workers.MainWorkerFragment;
import ph.com.gs3.formalistics.fragments.workers.MainWorkerFragment.MainWorkerFragmentListener;
import ph.com.gs3.formalistics.fragments.workers.MainWorkerFragment.SessionStatus;
import ph.com.gs3.formalistics.interfaces.CallbackCommand;
import ph.com.gs3.formalistics.managers.FLNotificationManager;
import ph.com.gs3.formalistics.managers.SessionManager.LoginResult;
import ph.com.gs3.formalistics.managers.SessionManager.Mode;
import ph.com.gs3.formalistics.model.dao.FormsDAO;
import ph.com.gs3.formalistics.model.dao.UserAccountsDAO;
import ph.com.gs3.formalistics.model.valueobjects.application.Form;
import ph.com.gs3.formalistics.model.valueobjects.application.FormStub;
import ph.com.gs3.formalistics.model.valueobjects.business.User;
import ph.com.gs3.formalistics.services.DocumentsUpdateService;
import ph.com.gs3.formalistics.services.FormsUpdateService;
import ph.com.gs3.formalistics.services.FormsUpdateService.FormsUpdateListener;
import ph.com.gs3.formalistics.services.FormsUpdateService.FormsUpdateServiceBinder;
import ph.com.gs3.formalistics.utilities.FLLogger;
import ph.com.gs3.formalistics.utilities.FLLogger.LogType;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.os.Bundle;
import android.os.IBinder;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;

/**
 * 
 * @author Ervinne Sodusta
 */
public class MainActivity extends FragmentActivity implements
		MainWorkerFragmentListener, MainViewActivityActionListener {

	public static final String TAG = MainActivity.class.getSimpleName();
	public static final String EXTRA_ACTIVE_USER = "main_active_user";

	public static final String EXTRA_INITIAL_FORM_FILTER_SELECTION = "form_filter_selection";
	public static final String EXTRA_SESSION_STATUS = "session_status";

	// Notification
	// TODO: make this configurable later
	private static final boolean ENABLE_NOTIFICATIONS = true;
	private FLNotificationManager notificationManager;

	// View Fragments & Dialogs
	private MainViewFragment mainViewFragment;

	private FormSelectionDialogFragment formSelectionDialogFragment;

	// Worker Fragments
	private MainWorkerFragment mainWorkerFragment;

	// Data Access Objects
	private FormsDAO formsDAO;
	private UserAccountsDAO userAccountsDAO;

	// Data Fields
	private ArrayList<FormStub> currentFormFilterSelection;

	// State Fields
	private SessionStatus sessionStatus;
	private User currentlyActiveUser;
	private boolean boundToFormsUpdateService = false;
	private boolean boundToDocumentsUpdateService = false;

	private boolean reauthenticationAlreadyRequested = false;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		initializeData();

		if (savedInstanceState == null) {
			mainViewFragment = MainViewFragment
					.createInstance(currentFormFilterSelection);
			getSupportFragmentManager().beginTransaction()
					.add(R.id.container, mainViewFragment).commit();
		}

		// initialize managers
		if (ENABLE_NOTIFICATIONS) {
			notificationManager = new FLNotificationManager(MainActivity.this);
		}

		initializeWorkerFragments();
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {

		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}

	@Override
	public boolean onOptionsItemSelected(MenuItem item) {
		// Handle action bar item clicks here. The action bar will
		// automatically handle clicks on the Home/Up button, so long
		// as you specify a parent activity in AndroidManifest.xml.
		int id = item.getItemId();

		switch (id) {
		case R.id.action_settings: {
			// TODO: open settings activity here
		}
			break;
		case R.id.action_tester: {
			Intent tester = new Intent(MainActivity.this, TesterActivity.class);
			startActivity(tester);
		}
			break;
		case R.id.action_create_document: {
			openSelectFormDialogForDocumentCreation();
		}
			break;
		}

		return super.onOptionsItemSelected(item);
	}

	@Override
	protected void onDestroy() {
		super.onDestroy();

		unbindAllBoundServices();
	}

	// ========================================================================
	// View Methods

	private void openSelectFormDialogForDocumentCreation() {

		if (formSelectionDialogFragment == null) {
			// lazy initialize the dialog fragment
			formSelectionDialogFragment = new FormSelectionDialogFragment();
		}

		if (boundToFormsUpdateService) {
			// forms are still updating
			formSelectionDialogFragment
					.setFormsAvailability(FormsAvailability.CURRENTLY_UPDATING);
		} else {
			if (currentFormFilterSelection.size() >= 1) {
				formSelectionDialogFragment
						.setFormsAvailability(FormsAvailability.HAS_FORMS);
			} else {
				formSelectionDialogFragment
						.setFormsAvailability(FormsAvailability.NO_FORMS_TO_SHOW);
			}

		}

		formSelectionDialogFragment.setFormStubs(currentFormFilterSelection);
		formSelectionDialogFragment.show(getFragmentManager(),
				FormSelectionDialogFragment.TAG);

		formSelectionDialogFragment
				.setOnFormSelectedCallback(new CallbackCommand<FormStub>() {

					@Override
					public void execute(FormStub selectedForm) {
						navigateToCreateDocumentView(selectedForm.dbId);
					}
				});

	}

	// ========================================================================
	// Navigation Methods

	private void navigateToCreateDocumentView(String formId) {

		Intent documentViewActivity = new Intent(MainActivity.this,
				DocumentActivity.class);
		documentViewActivity.putExtra(DocumentActivity.EXTRA_DOCUMENT_TYPE,
				DocumentType.NEW_DOCUMENT);
		documentViewActivity
				.putExtra(DocumentActivity.EXTRA_FORM_DB_ID, formId);
		documentViewActivity.putExtra(DocumentActivity.EXTRA_SERVER,
				currentlyActiveUser.getAccountServer());
		startActivity(documentViewActivity);

	}

	// ========================================================================
	// Initializer Methods

	private void initializeData() {

		formsDAO = new FormsDAO(this);
		userAccountsDAO = new UserAccountsDAO(this);

		formsDAO.open();
		currentFormFilterSelection = (ArrayList<FormStub>) formsDAO
				.getAllFormIdNameStub();
		formsDAO.close();

		if (getIntent().hasExtra(EXTRA_ACTIVE_USER)
				&& getIntent().getExtras().get(EXTRA_ACTIVE_USER) != null) {
			// this activity was opened from login activity, use the user that
			// was logged
			// in
			currentlyActiveUser = getUserFromExtras();
			sessionStatus = SessionStatus.LOGGED_IN;

			startFormsUpdateService();
		} else {
			// this activity will go through auto login, use the last active
			// user as the
			// currently active user.
			currentlyActiveUser = getLastActiveUser();
			sessionStatus = SessionStatus.NO_SESSION;
		}

	}

	private void initializeWorkerFragments() {

		FragmentManager fragmentMan = getSupportFragmentManager();

		mainWorkerFragment = (MainWorkerFragment) fragmentMan
				.findFragmentByTag(MainWorkerFragment.TAG);

		if (mainWorkerFragment == null) {
			mainWorkerFragment = MainWorkerFragment.createInstance(
					currentlyActiveUser, sessionStatus);
			fragmentMan.beginTransaction()
					.add(mainWorkerFragment, MainWorkerFragment.TAG).commit();

			// the code below was moved to the onModeChanged implementation
			// method of
			// session manager
			// if (sessionStatus == SessionStatus.LOGGED_IN) {
			// startFormsUpdateService();
			// }

		}

	}

	// ========================================================================
	// Functional Methods

	private void onFormUpdatingFinished() {
		startDocumentsUpdateService();
	}

	private void unbindAllBoundServices() {
		if (boundToFormsUpdateService) {
			unbindService(fadUpdateServiceConn);
			boundToFormsUpdateService = false;

			FLLogger.d(TAG, "Forms Update Service Unbound");
		}

		if (boundToDocumentsUpdateService) {
			// unbind documents update service here
		}
	}

	private User getUserFromExtras() {

		Intent intent = getIntent();

		if (intent.hasExtra(EXTRA_ACTIVE_USER)) {
			return (User) intent.getExtras().get(EXTRA_ACTIVE_USER);
		}

		return null;

	}

	private User getLastActiveUser() {

		userAccountsDAO.open();
		User user = userAccountsDAO.getActiveUserAccount();
		userAccountsDAO.close();

		return user;
	}

	private void startFormsUpdateService() {

		if (!boundToFormsUpdateService) {
			// start forms and document download service
			Intent intent = new Intent(MainActivity.this,
					FormsUpdateService.class);
			intent.putExtra(FormsUpdateService.EXTRA_SERVER,
					currentlyActiveUser.getAccountServer());

			bindService(intent, fadUpdateServiceConn, Context.BIND_AUTO_CREATE);
			startService(intent);

			FLLogger.d(TAG, "Forms update service started");
			boundToFormsUpdateService = true;
		} else {
			FLLogger.d(TAG,
					"Attempted to start forms update service, but it is already running");
		}

	}

	private void startDocumentsUpdateService() {

		if (!boundToDocumentsUpdateService) {

			Intent intent = new Intent(MainActivity.this,
					DocumentsUpdateService.class);
			intent.putExtra(DocumentsUpdateService.EXTRA_SERVER,
					currentlyActiveUser.getAccountServer());

			bindService(intent, documentsUpdateServiceConn,
					Context.BIND_AUTO_CREATE);
			startService(intent);

			FLLogger.d(TAG, "Documents update service started");
			boundToFormsUpdateService = true;

		} else {
			FLLogger.d(TAG,
					"Attempted to start documents update service, but it is already running");
		}

	}

	// ========================================================================
	// Implementation Methods (MainViewFragment)

	@Override
	public void onFormFilterChanged(FormStub stub) {
		FLLogger.d(TAG, "Filter changed: " + stub);

	}

	// ========================================================================
	// Implementation Methods (MainWorkerFragment)

	@Override
	public void onModeChanged(Mode mode) {

		if (mode == Mode.OFFLINE) {
			/*Toast.makeText(MainActivity.this,
					"You are working in offline mode. (Change this later)",
					Toast.LENGTH_LONG).show();*/
			mainViewFragment.notifyOfflineMode();
		} else {
			/*Toast.makeText(MainActivity.this,
					"You are working in online mode. (Change this later)",
					Toast.LENGTH_LONG).show();*/				
			startFormsUpdateService();
			mainViewFragment.notifyOnlineMode();
		}

	}
	

	@Override
	public void onUserNeedsToReauthenticate(LoginResult loginResult) {
		// go back to login activity and close this activity

		Intent loginActivity = new Intent(MainActivity.this,
				LoginActivity.class);
		loginActivity.putExtra(LoginActivity.EXTRA_STATUS,
				LoginActivity.Status.NEEDS_REAUTHENTICATION);
		loginActivity.putExtra(LoginActivity.EXTRA_LOGIN_RESULT, loginResult);
		startActivity(loginActivity);

		finish();

	}

	@Override
	public void onApplicationReAuthenticated(User user) {
		notificationManager.dismissSigningIn();

		currentlyActiveUser = user;
		startFormsUpdateService();

	}

	@Override
	public void onApplicationIsReAuthenticating(User user) {
		FLLogger.log(TAG, LogType.MESSAGE, user.getEmail() + " is logging in");
		notificationManager.notifySigningIn(user.getEmail());
	}

	// ========================================================================
	// Implementation Classes and Objects

	protected FormsUpdateListener formsUpdateListener = new FormsUpdateListener() {

		@Override
		public void onFinishedUpdatingForms(List<Form> updatedForms) {

			FLLogger.d(TAG, "Finished updating forms");

			formsDAO.open();
			mainViewFragment.setFormFilterSelection(formsDAO
					.getAllFormIdNameStub());
			currentFormFilterSelection = (ArrayList<FormStub>) formsDAO
					.getAllFormIdNameStub();
			formsDAO.close();

			unbindAllBoundServices();

			onFormUpdatingFinished();
		}

		@Override
		public void onFailedUpdatingForms(Exception e) {
			// Toast.makeText(MainActivity.this,
			// "Failed updating forms, " + e.getMessage() +
			// " \n(Replace this later)",
			// Toast.LENGTH_LONG).show();

			if ("You must be logged in to access this service".equals(e
					.getMessage()) && !reauthenticationAlreadyRequested) {
				// request reauthentication

				mainWorkerFragment.notifySessionLost();
				reauthenticationAlreadyRequested = true;

				FLLogger.d(TAG,
						"Error updating forms, trying to reauthenticate");
			}

			FLLogger.log(TAG, LogType.ERROR,
					"Error downloading forms: " + e.getMessage());
			// FLLogger.log(TAG, LogType.ERROR, "Error downloading forms");

			unbindAllBoundServices();

			onFormUpdatingFinished();
		}
	};

	protected ServiceConnection fadUpdateServiceConn = new ServiceConnection() {

		FormsUpdateServiceBinder formsUpdateServiceBinder;

		@Override
		public void onServiceConnected(ComponentName name, IBinder binder) {

			formsUpdateServiceBinder = (FormsUpdateServiceBinder) binder;
			formsUpdateServiceBinder.registerListener(formsUpdateListener);

			FLLogger.d(TAG, "Service " + name + " bound and connected.");
			// finish();
		}

		@Override
		public void onServiceDisconnected(ComponentName name) {
			formsUpdateServiceBinder.removeListener(formsUpdateListener);

			unbindAllBoundServices();
		}
	};

	protected ServiceConnection documentsUpdateServiceConn = new ServiceConnection() {

		@Override
		public void onServiceDisconnected(ComponentName name) {
			// TODO Auto-generated method stub

		}

		@Override
		public void onServiceConnected(ComponentName name, IBinder service) {
			// TODO Auto-generated method stub

		}
	};

	protected DocumentListItemActionListener documentListItemActionListener = new DocumentListItemActionListener() {

		@Override
		public void onClickedActions(int position) {
			// TODO Auto-generated method stub

		}

		@Override
		public void onClickedComments(int position) {
			Intent i = new Intent(MainActivity.this, CommentActivity.class);
			startActivity(i);
		}

		@Override
		public void onStarredDocument(int position) {
			// TODO Auto-generated method stub

		}

		@Override
		public void onUnstarredDocument(int position) {
			// TODO Auto-generated method stub

		}

	};

}
